﻿using System.Collections.Generic;
using System.Drawing;
using CNStreaming;
using CNUtility;

namespace CNShapes
{
    /// <summary>
    /// This shape stores freehand drawings. This is just a collection of points
    /// that should be rendered with lines between them.
    /// </summary><remarks>
    /// Note on addPoint
    /// ----------------
    /// To add a point to the shape you should always call the addPoint(point) method.
    /// You should not add points using Points.Add(point). This is because we may need 
    /// to update the  shape's bounds and raise events when points are added, so it 
    /// must be done through a method that we have control over.
    /// </remarks>
    public class Shape_Freehand : ShapeBase_DrawnWithPen
    {
        #region Public properties

        /// <summary>
        /// Collection of points that make up this shape.
        /// </summary>
        public IList<PointF> Line
        {
            get { return m_Line; }
        }

        #endregion

        #region IStreamable methods

        /// <summary>
        /// Serializes the object's data into the stream passed in.
        /// </summary>
        public override void toStream(MessageStream stream)
        {
            base.toStream(stream);

            // We add the collection of points...
            stream.writeInt(m_Line.Count);
            foreach (PointF pt in m_Line)
            {
                stream.writePointF(pt);
            }
        }

        /// <summary>
        /// Fills in the object's data from the stream passed in.
        /// </summary>
        public override void fromStream(MessageStream stream, int iVersion)
        {
            base.fromStream(stream, iVersion);

            // We read the collection of points...
            m_Line.Clear();
            int iCount = stream.readInt();
            for (int i = 0; i < iCount; ++i)
            {
                PointF point = stream.readPointF();
                m_Line.Add(point);
                updateBounds(point);
            }
        }

        /// <summary>
        /// Gets the version number.
        /// </summary>
        public override int getMessageVersion()
        {
            return 1;
        }

        #endregion

        #region Shape members

        /// <summary>
        /// Returns the bounds of this shape.
        /// </summary>
        public override RectangleF getBounds(Graphics graphics)
        {
            return m_Bounds;
        }

        #endregion

        #region Public methods

        /// <summary>
        /// Adds a point to the drawing.
        /// </summary>
        public void addPoint(PointF point, ActionOrigin actionOrigin)
        {
            // We add the point to the current line and update the bounds if necessary...
            m_Line.Add(point);
            updateBounds(point);

            // We raise an event to say that the shape has changed...
            raiseShapeEditedEvent(actionOrigin);
        }

        #endregion

        #region Private functions

        /// <summary>
        /// Updates the bounds to include the point passed in.
        /// </summary>
        protected void updateBounds(PointF point)
        {
            // Have any bounds been set yet? If not, the bounds are just the new point...
            if (m_bBoundsInitialized == false)
            {
                m_Bounds.X = point.X;
                m_Bounds.Y = point.Y;
                m_Bounds.Width = 0;
                m_Bounds.Height = 0;
                m_bBoundsInitialized = true;
                return;
            }

            // There are already existing bounds. We check if the point falls outside
            // them, and we update the bounds if it does...
            float boundsLeft = m_Bounds.Left;
            float boundsTop = m_Bounds.Top;
            float boundsRight = m_Bounds.Right;
            float boundsBottom = m_Bounds.Bottom;
            if (point.X < boundsLeft)
            {
                boundsLeft = point.X;
            }
            if (point.X > boundsRight)
            {
                boundsRight = point.X;
            }
            if (point.Y < boundsTop)
            {
                boundsTop = point.Y;
            }
            if (point.Y > boundsBottom)
            {
                boundsBottom = point.Y;
            }
            m_Bounds.X = boundsLeft;
            m_Bounds.Y = boundsTop;
            m_Bounds.Width = boundsRight - boundsLeft;
            m_Bounds.Height = boundsBottom - boundsTop;
        }

        #endregion

        #region Private data

        // The collection of points that make up this freehand line...
        private IList<PointF> m_Line = new List<PointF>();

        private bool m_bBoundsInitialized = false;
        private RectangleF m_Bounds = new RectangleF();

        #endregion
    }
}
